package com.sdhs.file.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import com.sdhs.common.core.domain.R;
import com.sdhs.common.core.domain.model.LoginUser;
import com.sdhs.common.core.text.CharsetKit;
import com.sdhs.common.exception.ServiceException;
import com.sdhs.common.utils.SecurityUtils;
import com.sdhs.common.utils.StringUtils;
import com.sdhs.file.config.MinioConfig;
import com.sdhs.file.config.OssConfig;
import com.sdhs.file.domain.SysFileEntity;
import com.sdhs.file.mapper.SysFileMapper;
import com.sdhs.file.service.ISysFileService;
import com.sdhs.file.utils.FileUploadUtils;
import com.sdhs.file.utils.MinioTemplate;
import com.sdhs.file.utils.OssTemplate;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import lombok.extern.log4j.Log4j2;

/**
 * Service业务层处理
 *
 * @author fangqisong
 * @date 2023-08-09
 */

@Primary
@Service
@Log4j2
public class SysFileServiceImpl implements ISysFileService {
    @Autowired
    private MinioTemplate minioTemplate;
    
    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private SysFileMapper sysFileMapper;

    @Autowired
    private MinioConfig minioConfig;
    
    @Autowired
    private OssConfig ossConfig;
    
//    @Autowired
//    private CephConfig cephConfig;

//    @Autowired
//    private MD5Util md5Util;
    
//    @Autowired
//    private CephTemplate cephTemplate;

    /**
     * 查询
     *
     * @param id 主键
     * @return
     */
    @Override
    public SysFileEntity selectSysFileById(Long id) {
        return sysFileMapper.selectSysFileById(id);
    }

    /**
     * 查询列表
     *
     * @param sysFile
     * @return
     */
    @Override
    public List<SysFileEntity> selectSysFileList(SysFileEntity sysFile) {
        return sysFileMapper.selectSysFileList(sysFile);
    }

    /**
     * 新增
     *
     * @param sysFile
     * @return 结果
     */
    @Override
    public int insertSysFile(SysFileEntity sysFile) {
        return sysFileMapper.insertSysFile(sysFile);
    }

    /**
     * 修改
     *
     * @param sysFile
     * @return 结果
     */
    @Override
    public int updateSysFile(SysFileEntity sysFile) {
        return sysFileMapper.updateSysFile(sysFile);
    }

    /**
     * 批量删除
     *
     * @param ids 需要删除的主键
     * @return 结果
     */
    @Override
    public int deleteSysFileByIds(String[] ids) {
        return sysFileMapper.deleteSysFileByIds(ids);
    }

    /**
     * 删除信息
     *
     * @param id 主键
     * @return 结果
     */
    @Override
    public int deleteSysFileById(String id) {
        return sysFileMapper.deleteSysFileById(id);
    }

//    @Override
//    public void upload_test(MultipartFile file, String folder, String fileName, String md5) {
//    	folder = StringUtils.defaultString(folder, cephConfig.getBucketName());
//        if (StringUtils.isNull(fileName)) {
//            fileName = file.getOriginalFilename();
//        }
//        // 防止文件重复，生成uuid做文件名
//        String id = StringUtils.getUUID();
//        String fileType = FilenameUtils.getExtension(fileName);
//        try {
//            String uploadName = FileUploadUtils.extractFilename(id, fileType);
//            // 开始上传
//            cephTemplate.putObjectSimple(folder, uploadName, file);
//        } catch (Exception e) {
//            log.error("文件上传异常-->>:{}", e);
//            throw new ServiceException("文件上传服务器失败");
//        }
//    }

//    @Override
//    public void download_test(String id, HttpServletResponse response) {
//        log.info("开始下载：{}", id);
//        response.setCharacterEncoding("UTF-8");
//        SysFileEntity sysFileEntity = sysFileMapper.selectById(id);
//        if (null != sysFileEntity) {
//        	cephTemplate.downloadFile(sysFileEntity.getFileFolder(), sysFileEntity.getFileNameFull(), sysFileEntity.getFileName(), sysFileEntity.getFileType(), response);
//        } else {
//            log.error("文件不存在：{}", id);
//            throw new RuntimeException("文件不存在");
//        }
//    }
    
    /**
     * 获取文件下载URL
     * @param id
     * @param expirationInSeconds
     * @return
     */
//    @Override
//    public String generatePresignedUrl(String id, Integer expirationInSeconds) {
//    	SysFileEntity sysFileEntity = sysFileMapper.selectById(id);
//        if (null != sysFileEntity) {
//        	return cephTemplate.generatePresignedUrl(sysFileEntity.getFileFolder(), sysFileEntity.getFileNameFull(), expirationInSeconds);
//        } else {
//            log.error("文件不存在：{}", id);
//            throw new RuntimeException("文件不存在");
//        }
//    }

    /**
     * Minio文件上传接口
     *
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public SysFileEntity uploadFile(MultipartFile file, String folder, String fileName, String md5) {
//        folder = StringUtils.defaultString(folder, minioConfig.getBucketName());
    	folder = StringUtils.defaultString(folder, ossConfig.getBucketName());
        if (StringUtils.isNull(fileName)) {
            fileName = file.getOriginalFilename();
        }
        // 防止文件重复，生成uuid做文件名
        String id = StringUtils.getUUID();
        String fileType = FilenameUtils.getExtension(fileName);
        SysFileEntity sysFileEntity = null;
        try {
            String uploadName = FileUploadUtils.extractFilename(id, fileType);
            // 开始上传
//            minioTemplate.putObject(folder, uploadName, file);
            // 获取文件url
//            String url = minioTemplate.getObjectUrl(folder, uploadName);
            String url = ossTemplate.putObjectSimple(folder, uploadName, file);
            sysFileEntity = SysFileEntity.builder()
                    .fileFolder(folder)
                    .fileNameFull(uploadName)
                    .fileName(fileName)
                    .fileType(fileType)
                    .url(url)
//                    .userId(SecurityUtils.getUserId())
                    .userName(SecurityUtils.getUsername())
                    // 上传者姓名
                    .createNickName(SecurityUtils.getNickName())
                    .md5(md5)
                    .fileKey(IdUtil.getSnowflakeNextIdStr())
                    .fileSize(file.getSize())
                    .build();
            sysFileEntity.setCreateBy(sysFileEntity.getUserName());
            sysFileEntity.setCreateTime(new Date());
            // 保存bean
            sysFileMapper.insertSysFile(sysFileEntity);
        } catch (Exception e) {
            log.error("文件上传异常-->>:{}", e);
            throw new ServiceException("文件上传服务器失败");
        }
        return sysFileEntity;
    }

    /**
     * 文件下载
     * @param id       文件id
     * @param response
     */
    @Override
    public void download(Long id, HttpServletResponse response) {
        log.info("开始下载：{}", id);
        response.setCharacterEncoding("UTF-8");
        SysFileEntity sysFileEntity = sysFileMapper.selectSysFileById(id);
        if (null != sysFileEntity) {
//            minioTemplate.downloadFile(sysFileEntity.getFileFolder(), sysFileEntity.getFileNameFull(), sysFileEntity.getFileName(), sysFileEntity.getFileType(), response);
        	ossTemplate.downloadFile(sysFileEntity.getFileFolder(), sysFileEntity.getFileNameFull(), sysFileEntity.getFileName(), sysFileEntity.getFileType(), response);
        } else {
            log.error("文件不存在：{}", id);
            throw new RuntimeException("文件不存在");
        }
    }


    /**
     * 根据文件ossId下载附件压缩包
     * @param ossIdList 文件id集合
     * @param response
     */
    @Override
    public void downloadByFileIds(List<Long> ossIdList, HttpServletResponse response) {
        if (CollectionUtil.isEmpty(ossIdList)) {
            return;
        }
        ServletOutputStream outputStream = null;
        ZipArchiveOutputStream zip;
        InputStream inputStream = null;
        try {
            //实例化ServletOutputStream对象
            outputStream = response.getOutputStream();
            zip = new ZipArchiveOutputStream(outputStream);
            //对所有条目使用 Zip64 扩展
            zip.setUseZip64(Zip64Mode.AsNeeded);
            zip.setEncoding(CharsetKit.UTF_8);
            ZipArchiveEntry zipArchiveEntry;
                // 只下载文件，文件目录根据pidNames自动生成
                for (Long id : ossIdList) {
                    SysFileEntity sysFileEntity = sysFileMapper.selectSysFileById(id);
                    if (null == sysFileEntity) {
                        continue;
                    }
                    inputStream = minioTemplate.downloadFileInputStream(sysFileEntity.getFileFolder(), sysFileEntity.getFileNameFull(), sysFileEntity.getFileName(), sysFileEntity.getFileType(), response);
                    //目录
                    zipArchiveEntry = new ZipArchiveEntry(sysFileEntity.getFileName());
                    zip.putArchiveEntry(zipArchiveEntry);
                    IOUtils.copy(inputStream, zip);
                }
            zip.closeArchiveEntry();
            //压缩结束
            zip.finish();
        } catch (IOException e) {
            throw new ServiceException("打包失败，失败原因是" + e.getMessage());
        } finally {
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(outputStream);
        }
    }



    /**
     * 根据url远程上传文件
     * @param fileUrl
     * @param folder
     * @param fileName
     * @return
     */
    @Override
    public SysFileEntity uploadFileByUrl(String fileUrl, String folder, String fileName) {
        final String buket = StringUtils.defaultString(folder, "default");
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        // 定义请求头的接收类型
        RequestCallback requestCallback = request -> request.getHeaders()
                .setAccept(Arrays.asList(MediaType.TEXT_HTML, MediaType.APPLICATION_XHTML_XML, MediaType.APPLICATION_XML, MediaType.APPLICATION_XML, MediaType.ALL));
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<Resource> httpEntity = new HttpEntity<Resource>(headers);
        ResponseEntity<byte[]> bytes = restTemplate.exchange(fileUrl, HttpMethod.GET, httpEntity, byte[].class);
        SysFileEntity sysFileEntity = null;
        // 对响应进行流式处理而不是将其全部加载到内存中
//        SysFileEntity sysFileEntity = restTemplate.execute(fileUrl, HttpMethod.GET, requestCallback, clientHttpResponse -> {
//            if (clientHttpResponse.getStatusCode() == HttpStatus.OK) {
        try {
//                    System.err.println(clientHttpResponse.getBody());
            // 防止文件重复，生成uuid做文件名
            String id = StringUtils.getUUID();
            String fileType = FilenameUtils.getExtension(fileName);
            String uploadName = id + "." + fileType;
//                    minioTemplate.putObject(buket, uploadName, clientHttpResponse.getBody());
            minioTemplate.putObject(buket, uploadName, new ByteArrayInputStream(bytes.getBody()));
            LoginUser loginUser = SecurityUtils.getLoginUser();
            // 获取文件url
            String url = minioTemplate.getObjectUrl(buket, uploadName);
            SysFileEntity bean = SysFileEntity.builder()
//                    .id(id)
                    .fileFolder(buket)
                    .fileName(fileName)
                    .fileType(fileType)
                    .url(url)
//                    .userId(loginUser.getUserId())
                    .userName(loginUser.getUsername())
                    .createNickName(loginUser.getUser().getNickName())
                    .build();
            // 保存bean
            sysFileMapper.insertSysFile(bean);
            return bean;
        } catch (Exception e) {
            log.error("文件上传MinIo异常：{}", e);
            throw new RuntimeException("文件上传MinIo异常");
        }
    }

    /**
     * 文件分享。最长7天
     *
     * @param id
     * @param expiry 60 * 60 * 24
     * @return
     * @author yzz
     * @date 2020/9/30 11:30
     */
//    @Override
//    public String shareFile(String id, Integer expiry) {
//        log.info("分享文件：{}", id);
//        String ret = sysFileMapper.selectSysFileById(id).map(sysFileEntity -> {
//                    String fileName = id + "." + sysFileEntity.getFileType();
//                    String url = null;
//                    // 文件下载
//                    try {
//                        url = minioTemplate.getPresignedObjectUrl(sysFileEntity.getFileFolder(), fileName, expiry);
//                    } catch (Exception e) {
//                        log.error("服务器文件分享接口异常：{}", e);
//                    }
//                    return url;
//                })
//                // 文件不存在抛出异常
//                .orElseThrow(() -> {
//                    log.error("文件不存在：{}", id);
//                    return new RuntimeException("文件不存在");
//                });
//        return ret;
//    }


    /**
     * 创建文件夹
     *
     * @param bucketName
     */
    @Override
    public void makeBucket(String bucketName) {
        try {
            minioTemplate.makeBucket(bucketName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 拷贝指定文件到指定目录
     *
     * @param bucketName
     * @param objectName
     * @param destBucketName
     * @param destObjectName
     */
    @Override
    public void copyObject(String bucketName, String objectName, String destBucketName, String destObjectName) {
        try {
            minioTemplate.copyObject(bucketName, objectName, destBucketName, destObjectName);
//            cephTemplate.copyProject(bucketName, objectName, destBucketName, destObjectName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 拷贝指定文件到指定目录
     *
     * @param bucketName
     * @param objectName
     * @param destBucketName
     * @param destObjectName
     */
    @Override
    public String copyObjectNew(String bucketName, String objectName, String destBucketName, String destObjectName) {
        try {
//            return cephTemplate.copyProject(bucketName, objectName, destBucketName, destObjectName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 根据md5获取文件信息
     *
     * @param md5
     * @param fileName
     * @return
     */
    @Override
    public R getFileByMd5(String md5, String fileName) {
        Assert.isTrue(StringUtils.isNotBlank(md5), "请传入md5值");
        Optional<SysFileEntity> sysFileEntityOptional = sysFileMapper.selectSysFileByMd5(md5);

        SysFileEntity sysFileEntity = sysFileEntityOptional.orElse(null);
        // TODO 此处是什么逻辑？
/*        if (sysFileEntity != null) {
            UserDetail user = SecurityUser.getUser();
            sysFileDTO.setCreator(user.getId().toString());
            this.baseDao.updateById(sysFileEntity.setFileName(fileName));
            sysFileDTO.setFileName(fileName);
        }*/
        return R.ok(sysFileEntity);
    }

    @Override
    public SysFileEntity uploadLocalFile(String filePath, String folder, String fileName, String md5, Long size) throws Exception {
        folder = StringUtils.defaultString(folder, minioConfig.getBucketName());
//        folder = StringUtils.defaultString(folder, cephConfig.getBucketName());
        // 防止文件重复，生成uuid做文件名
        String id = StringUtils.getUUID();
        String fileType = FilenameUtils.getExtension(fileName);
        SysFileEntity sysFileEntity = null;
        try {
            String uploadName = FileUploadUtils.extractFilename(id, fileType);
            // 开始上传
            String url = minioTemplate.putObjectUrl(folder, uploadName, filePath);
//            String url = cephTemplate.putObjectByFilePath(folder, uploadName, filePath);
            sysFileEntity = SysFileEntity.builder()
                    .fileFolder(folder)
                    .fileNameFull(uploadName)
                    .fileName(fileName)
                    .fileType(fileType)
                    .url(url)
//                    .userId(SecurityUtils.getUserId())
                    .userName(SecurityUtils.getUsername())
                    .createNickName(SecurityUtils.getNickName())
                    .md5(md5)
                    .fileKey(IdUtil.getSnowflakeNextIdStr())
                    .fileSize(size)
                    .build();
            sysFileEntity.setCreateBy(sysFileEntity.getUserName());
            sysFileEntity.setCreateTime(new Date());
            // 保存bean
            sysFileMapper.insertSysFile(sysFileEntity);
        } catch (Exception e) {
            log.error("文件上传异常-->>:{}", e);
            throw new ServiceException("文件上传服务器失败");
        }
        return sysFileEntity;
    }

}

